package com.kiven.utils

import com.ruiyu.jsontodart.ClassDefinition
import com.ruiyu.jsontodart.Dependency
import com.ruiyu.jsontodart.utils.*
import com.ruiyu.utils.createJsonFacParesFunc
import com.ruiyu.utils.createjsonListParseFunc
import com.ruiyu.utils.toUpperCaseFirstOne

/**
 * 转化Dart文件内容的工具
 */
class ClassDefinition(val name: String, val privateFields: Boolean = false) {
    val fields = mutableMapOf<String, TypeDefinition>()

    // 获取fields中的非简单数据类型
    val dependencies: List<Dependency>
        get() {
            val dependenciesList = mutableListOf<Dependency>()
            val keys = fields.keys
            keys.forEach { k ->
                if (fields[k]!!.isPrimitive.not()) {
                    dependenciesList.add(Dependency(k, fields[k]!!))
                }
            }
            return dependenciesList;
        }

    /*private val isOpenNullSafety:Boolean
    private val isOpenNullAble:Boolean
    init {
        val settings = ServiceManager.getService(Settings::class.java)
        isOpenNullSafety= settings.isOpenNullSafety == true
        isOpenNullAble = settings.isOpenNullAble == true
    }*/

    fun addField(key: String, typeDef: TypeDefinition) {
        fields[key] = typeDef
    }

    fun hasField(otherField: TypeDefinition): Boolean {
        return fields.keys.firstOrNull { k -> fields[k] == otherField } != null
    }

    override operator fun equals(other: Any?): Boolean {
        if (other is ClassDefinition) {
            if (name != other.name) {
                return false;
            }
            return fields.keys.firstOrNull { k ->
                other.fields.keys.firstOrNull { ok ->
                    fields[k] == other.fields[ok]
                } == null
            } == null
        }
        return false
    }

    fun _addTypeDef(typeDef: TypeDefinition, sb: StringBuffer) {
        if (typeDef.name == "Null") {
            sb.append("dynamic")
        } else {
            sb.append(typeDef.name)
        }

        if (typeDef.subtype != null) {
            //如果是list,就把名字修改成单数
            sb.append("<${Inflector.getInstance().singularize(typeDef.subtype!!)}>")
        }
    }

    //字段的集合
    val _fieldList: String
        get() {
//            val prefix = if(isOpenNullSafety && !isOpenNullAble) "late " else ""
//            val suffix = if(isOpenNullSafety && isOpenNullAble) "?" else ""
            return fields.keys.map { key ->
                val f = fields[key]!!
                val fieldName = f.fieldName ?: fixFieldName(key, f, privateFields)
                val sb = StringBuffer();
                //如果驼峰命名后不一致,才这样
                if (fieldName != key) {
                    sb.append('\t')
                    sb.append("@JSONField(name: \"${key}\")\n")
                }
                sb.append('\t')
//                sb.append(prefix)
                if (f.isLate) sb.append("late ")
                _addTypeDef(f, sb)
//                sb.append(suffix)
                if (f.isNullable) sb.append("?")
                sb.append(" $fieldName")
                if (f.defaultValue != null) sb.append(" ${f.defaultValue}")
                sb.append(";")
                return@map sb.toString()
            }.joinToString("\n")
        }

    val _gettersSetters: String
        get() {
            return fields.keys.map { key ->
                val f = fields[key]!!
                val publicFieldName = f.fieldName ?: fixFieldName(key, f, false);
                val privateFieldName = f.fieldName ?: fixFieldName(key, f, true);
                val sb = StringBuffer();
                sb.append('\t');
                _addTypeDef(f, sb);
                sb.append(" get $publicFieldName => $privateFieldName;\n\tset $publicFieldName(")
                _addTypeDef(f, sb)
                sb.append(" $publicFieldName) => $privateFieldName = $publicFieldName;")
                return@map sb.toString()
            }.joinToString("\n")
        }


    val _defaultPrivateConstructor: String
        get() {
            val sb = StringBuffer()
            sb.append("\t$name({")
            var i = 0;
            var len = fields.keys.size - 1;
            fields.keys.forEach { key ->
                val f = fields[key]!!
                val publicFieldName = f.fieldName ?: fixFieldName(key, f, false);
                val privateFieldName = f.fieldName ?: fixFieldName(key, f, true);
                _addTypeDef(f, sb);
                sb.append(" $publicFieldName")
                if (i != len) {
                    sb.append(", ")
                }
                i++;
            }
            sb.append("}) {\n")
            fields.keys.forEach { key ->
                val f = fields[key]!!
                val publicFieldName = f.fieldName ?: fixFieldName(key, f, false);
                val privateFieldName = f.fieldName ?: fixFieldName(key, f, true);
                sb.append("this.$privateFieldName = $publicFieldName;\n")
            }
            sb.append('}');
            return sb.toString();
        }

    val _defaultConstructor: String
        get() {
            val sb = StringBuffer()
            sb.append("\t$name({")
            var i = 0;
            val len = fields.keys.size - 1

            fields.forEach { (key, f) ->
                val fieldName = f.fieldName ?: fixFieldName(key, f, privateFields);
                sb.append("\n\t\tthis.$fieldName")
                if (!f.isNullable) {
                    if (f.defaultValue == null) {
                        val nv = newDefaultValue(f.name)
                        if (nv != null)
                            sb.append(" = $nv")
                    } else {
                        sb.append(" = ${f.defaultValue}")
                    }
                }
                if (i != len) {
                    sb.append(", ")
                }
                i++
            }
            sb.append("});");
            return sb.toString()
        }


    val _fromMapFunc: String
        get() {
            // 没有 late和?修饰的字段，必须在构造方法中
            val a1 = mutableMapOf<String, TypeDefinition>()
            val a2 = mutableMapOf<String, TypeDefinition>()
            fields.forEach { (k, v) ->
                if (!v.isFinal && (v.isLate || v.isNullable || v.defaultValue != null)) {
                    a2[k] = v
                } else a1[k] = v
            }

            val sb = StringBuffer()
            sb.append("\t$name")
            sb.append(".fromMap(Map<String, dynamic> json)")

            if (a1.isNotEmpty()) {
                sb.append(" :\n")
                var count = 0
                a1.forEach { (k, v) ->
                    if (count++ > 0) {
                        sb.append(",\n")
                    }
                    sb.append("\t\t${v.jsonParseExpression(k, privateFields)}")
                }
            }

            if (a2.isEmpty()) {
                sb.append(";")
            } else {
                sb.append(" {\n")
                a2.keys.forEach { k ->
                    sb.append("\t\t${fields[k]?.jsonParseExpression(k, privateFields)};\n")
                }
                sb.append("\t}")
            }
            return sb.toString();
        }

    val _toMapFunc: String
        get() {
            val sb = StringBuffer();
            sb.append("\tMap<String, dynamic> toMap() {\n\t\tfinal data = <String, dynamic>{};\n");
            fields.keys.forEach { k ->
                sb.append("\t\t${fields[k]?.toJsonExpression(k, privateFields)}\n");
            }
            sb.append("\t\treturn data;\n");
            sb.append("\t}");
            return sb.toString();
        }
    val _jsonListParseFunc: String
        get() {
            return createjsonListParseFunc(name)
        }
    val _jsonFacParesFunc: String
        get() {
            return createJsonFacParesFunc(name)
        }

    override fun toString(): String {
        /*if (privateFields) {
            return "class $name {\n$_fieldList\n\n$_defaultPrivateConstructor\n\n$_gettersSetters\n\n$_jsonParseFunc\n\n$_jsonGenFunc\n}\n";
        } else {
            return "class $name {\n$_fieldList\n\n$_defaultConstructor\n\n$_jsonParseFunc\n\n$_jsonGenFunc\n\n$_jsonListParseFunc\n\n$_jsonFacParesFunc\n}\n";
        }*/
        return if (!fields.values.all { it.isNullable }) {
            "class $name {\n$_fieldList\n\n$_fromMapFunc\n\n$_toMapFunc\n\n$_jsonListParseFunc\n\n$_jsonFacParesFunc\n}\n";
        } else
            "class $name {\n$_fieldList\n\n$_defaultConstructor\n\n$_fromMapFunc\n\n$_toMapFunc\n\n$_jsonListParseFunc\n\n$_jsonFacParesFunc\n}\n";
    }
}


class Dependency(var name: String, var typeDef: TypeDefinition) {
    val className: String
        get() {
            return camelCase(name)
        }

    override fun toString(): String {
        return "name = ${name} ,typeDef = ${typeDef}"
    }
}

class TypeDefinition(
    var name: String,
    var subtype: String? = null,
    val fieldName: String? = null,
    val isLate: Boolean = false,
    val isNullable: Boolean = false,
    val isFinal: Boolean = false,
    val defaultValue: String? = null
) {

    // 是否是简单数据类型
    val isPrimitive: Boolean = if (subtype == null) {
        isPrimitiveType(name)
    } else {
        isPrimitiveType("$name<${subtype!!.toUpperCaseFirstOne()}>")
    }

    // 是否是简单数据类型的List
    private val isPrimitiveList = isPrimitive && name == "List"

    companion object {
        fun fromDynamic(
            obj: Any?,
            fieldName: String? = null,
            isLate: Boolean = false,
            isNullable: Boolean = false
        ): TypeDefinition {
            val type = getTypeName(obj)
            if (type == "List") {
                val list = obj as List<*>
                val firstElementType = if (list.isNotEmpty()) {
                    getTypeName(list[0])
                } else {
                    // when array is empty insert Null just to warn the user
                    "Null"
                }
                return TypeDefinition(type, firstElementType, fieldName, isLate, isNullable)
            }
            return TypeDefinition(type, null, fieldName, isLate, isNullable)
        }
    }


    override operator fun equals(other: Any?): Boolean {
        if (other is TypeDefinition) {
            return name == other.name && subtype == other.subtype;
        }
        return false;
    }

    fun _buildParseClass(expression: String): String {
        val properType = if (subtype != null) subtype else name
        return "$properType.fromMap($expression)"
    }

    fun _buildToJsonClass(expression: String): String {
        return "$expression.toMap()"
    }

    fun jsonParseExpression(key: String, privateField: Boolean): String {
        val jsonKey = "json['$key']"
        val fieldKey = fieldName ?: fixFieldName(key, this, privateField)
        when {
            isPrimitive -> {
                if (name == "List") {
                    return "$fieldKey = json['$key']?.cast<$subtype>() ?? []"
                }

                // 可空的情况下，不判断值是否为空，优化代码
                if (isNullable) return "$fieldKey = json['$key']"
                else if (!isLate) {
                    if (defaultValue == null) {
                        val nv = newDefaultValue(name)
                        if (nv != null)
                            return "$fieldKey = json['$key'] ?? $nv"
                    } else {
                        return "$fieldKey = json['$key'] ?? $defaultValue"
                    }
                }

                return "if (json['$key'] != null) $fieldKey = json['$key']"
            }
            name == "List" -> { // list of class  //如果是list,就把名字修改成单数
                /*val value = if (subtype == "Null") "" else """
			        (json['$key'] as List).forEach((v) { $fieldKey.add(new ${Inflector.getInstance().singularize(subtype!!)}.fromMap(v)); });
                """.trimIndent()
                return "if (json['$key'] != null) {" +
                        "\n\t\t\t$fieldKey = <${Inflector.getInstance().singularize(subtype!!)}>[];\n\t\t\t$value\n\t\t" +
                        "} else { $fieldKey = []; }"*/
                return "$fieldKey = (json['$key'] as List?)?.map((e) => new ${
                    Inflector.getInstance().singularize(subtype!!)
                }.fromMap(e)).toList() ?? []"
            }
            else -> // class
                return if (isNullable || (!isLate && defaultValue == null))
                    "$fieldKey = json['$key'] != null ? ${_buildParseClass(jsonKey)} : null"
                else "if (json['$key'] != null) $fieldKey = ${_buildParseClass(jsonKey)}"
        }
    }


    fun toJsonExpression(key: String, privateField: Boolean): String {
        val fieldKey = fieldName ?: fixFieldName(key, this, privateField)
        when {
            isPrimitive -> {
                return "data['$key'] = $fieldKey;"
            }
            name == "List" -> {
                val wen = if (isNullable) "?" else ""
                val value = if (subtype == "Null") "[]" else "$fieldKey$wen.map((v) => ${_buildToJsonClass("v")}).toList()"
                // class list

                return if (isNullable)
                    """
                            if ($fieldKey != null) {
                                data['$key'] =  $value;
                            }"""
                else "data['$key'] =  $value;"
            }
            else -> {
                // class
                return if (isNullable)
                    """if ($fieldKey != null) {
                  data['$key'] = ${_buildToJsonClass("$fieldKey?")};
                }"""
                else "data['$key'] = ${_buildToJsonClass(fieldKey)};"
            }
        }
    }

    override fun hashCode(): Int {
        var result = name.hashCode()
        result = 31 * result + (subtype?.hashCode() ?: 0)
        result = 31 * result + isPrimitive.hashCode()
        result = 31 * result + isPrimitiveList.hashCode()
        return result
    }

    override fun toString(): String {
        return "TypeDefinition(name='$name', subtype=$subtype, isPrimitive=$isPrimitive, isPrimitiveList=$isPrimitiveList)"
    }
}